package events.containers2;

import java.util.Arrays;
import java.util.Collection;
import java.util.EventObject;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

import javax.swing.ListModel;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;

import zerdlib.events.DefaultListenable;
import zerdlib.events.GenericListenable;
import zerdlib.events.GenericListener;
import zerdlib.events.WeakGenericListener;

import events.listenerWrappers.GenericListenerWrapper;

public class SortedSetModel<E> extends DefaultListenable implements SortedSet<E>, GenericListener, ListModel<E> {
	private TreeMap<Object, E> data;

	public SortedSetModel(java.util.Comparator<Object> comparator) { //TODO restreindre comparaotor
		super();
		data = new TreeMap<>(comparator);
	}

	@Override
	public int getSize() {
		return data.size();
	}

	@SuppressWarnings("unchecked")
	@Override
	public E getElementAt(int index) {
		if(index < 0 || index >= size())
			throw new IndexOutOfBoundsException("size="+size()+"\t index="+index);
		Iterator<Object> it = data.keySet().iterator();
		Object ret=null;
		for(int i=0 ; i<index ; i++)
			ret = it.next();
		return (E)ret;
	}

	public int indexOf(Object o) {
		Iterator<Object> it = data.keySet().iterator();
		for(int i=0 ; it.hasNext() ; i++)
			if(data.comparator().compare(it.next(), o) == 0)
				return i;
		return -1;
	}

	@Override
	public int size() {
		return data.size();
	}

	@Override
	public boolean isEmpty() {
		return data.isEmpty();
	}

	@Override
	public boolean contains(Object o) {
		return data.containsKey(o);
	}

	@SuppressWarnings("unchecked")
	public E get(Object o) {
		if(contains(o))
			return (E) data.get(o);
		else
			return null;
	}
	
	@Override
	public Iterator<E> iterator() {
		return new Iterator<E>() {
			private int index = -1;
			private Iterator<E> wrapped = data.values().iterator();
			@Override
			public boolean hasNext() {
				return wrapped.hasNext();
			}

			@SuppressWarnings("unchecked")
			@Override
			public E next() {
				return (E)data[++index];
			}

			@Override
			public void remove() {
				SortedSetModel.this.remove(index);
			}
		};
	}

	@Override
	public Object[] toArray() {
		return data.values().toArray();
	}

	@Override
	public <T> T[] toArray(T[] a) {
		return data.values().toArray(a);
	}

	@Override
	public boolean add(E e) {
		if(!contains(e)) {
			data.put(e, e);
			int index = indexOf(e);
			fireEvent(new ListDataEvent(this, ListDataEvent.INTERVAL_ADDED, index, index));
			if(e instanceof GenericListenable)
				WeakGenericListener.addWeakListener((GenericListenable) e, this);
			return true;
		} else {
			return false;
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public boolean remove(Object o) {
		if(contains(o)) {
			int index = indexOf(o);
			E removed = data.remove(o);
			if(removed instanceof GenericListenable)
				((GenericListenable) removed).removeListener(this);
			fireEvent(new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, index, index));
			return true;
		} else {
			return false;
		}
	}

	@Override
	public boolean containsAll(Collection<?> c) {
		return data.keySet().containsAll(c);
	}

	@Override
	public boolean addAll(Collection<? extends E> c) {
		boolean ret = false;
		int minIndex=size(), maxIndex=-1;
		for(E e : c) {
			if(!contains(e)) {
				data.put(e, e);
				int index = indexOf(e);
				minIndex = Math.min(minIndex, index);
				maxIndex = Math.max(maxIndex+1, index);

				if(e instanceof GenericListenable)
					WeakGenericListener.addWeakListener((GenericListenable) e, this);
				ret=true;
			}
		}

		if(ret)
			fireEvent(new ListDataEvent(this, ListDataEvent.INTERVAL_ADDED, minIndex, maxIndex));
		return ret;
	}


	@Override
	public synchronized boolean retainAll(Collection<?> c) {
		boolean ret = false;
		int minIndex=size(), maxIndex=-1;
		Collection<E> values = data.values();
		Iterator<E> it = values.iterator();
		for(int i=0 ; it.hasNext() ; i++) {
			E e = it.next();
			if(!c.contains(e)) {
				minIndex = Math.min(minIndex, i);
				maxIndex = i;
				if(e instanceof GenericListenable)
					((GenericListenable) e).removeListener(this);
				it.remove();
				ret = true;
			}
		}
		
		if(ret)
			fireEvent(new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, minIndex, maxIndex));
		return ret;
	}

	@Override
	public synchronized boolean removeAll(Collection<?> c) {
		boolean ret = false;
		int minIndex=size(), maxIndex=-1;
		Collection<E> values = data.values();
		Iterator<E> it = values.iterator();
		for(int i=0 ; it.hasNext() ; i++) {
			E e = it.next();
			if(c.contains(e)) {
				minIndex = Math.min(minIndex, i);
				maxIndex = i;
				if(e instanceof GenericListenable)
					((GenericListenable) e).removeListener(this);
				it.remove();
				ret = true;
			}
		}
		
		if(ret)
			fireEvent(new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, minIndex, maxIndex));
		return ret;
	}

	@Override
	public synchronized void clear() {
		int oldSize = size();
		for(E e : data.values())
			if(e instanceof GenericListenable)
				((GenericListenable) e).removeListener(this);
		
		data.clear();
		
		if(oldSize>0)
			fireEvent(new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, 0, oldSize-1));
	}

	@SuppressWarnings("unchecked")
	@Override
	public void onEvent(EventObject ev) {
		E source = (E) ev.getSource();
		
		int oldIndex = 0;
		Iterator<Object> it = data.keySet().iterator();
		for( ; it.hasNext() ; oldIndex++)
			if(it.next()==source)
				break;
		
		data.remove(source);
		data.put(source, source);
		int newIndex = indexOf(source);
		
		fireEvent(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, oldIndex, newIndex));
	}

	@Override
	public void addListDataListener(ListDataListener l) {
		addListener(new GenericListenerWrapper<ListDataListener>(l) {
			@Override
			public void onEvent(EventObject ev_) {
				ListDataEvent ev = (ListDataEvent) ev_;
				switch (ev.getType()) {
				case ListDataEvent.CONTENTS_CHANGED:
					wrapped.contentsChanged(ev);
					break;
				case ListDataEvent.INTERVAL_ADDED:
					wrapped.intervalAdded(ev);
					break;
				case ListDataEvent.INTERVAL_REMOVED:
					wrapped.intervalRemoved(ev);
					break;
				}
			}
		});
	}

	@Override
	public void removeListDataListener(ListDataListener l) {
		removeListener(l);
	}

	@Override
	public java.util.Comparator<? super E> comparator() {
		return data.comparator();
	}

	@Override
	public SortedSet<E> subSet(E fromElement, E toElement) {
		throw new UnsupportedOperationException();
	}

	@Override
	public SortedSet<E> headSet(E toElement) {
		throw new UnsupportedOperationException();
	}

	@Override
	public SortedSet<E> tailSet(E fromElement) {
		throw new UnsupportedOperationException();
	}

	@Override
	public E first() {
		throw new UnsupportedOperationException();
	}

	@Override
	public E last() {
		throw new UnsupportedOperationException();
	}
}
